/*
 * ButtonControl.c
 *
 *  Created on: 25.09.2011
 *      Author: wink
 */

#include <avr/io.h>
#include <string.h>
#include "Globals.h"
#include "HwAbstraction.h"
#include "ButtonControl.h"

#define BUTTON_PORT PINC
#define BUTTON_BIT PINC0
#define CAP_MAX 16 /* TODO (11-09-26): meaning ??? */
#define CAP_HIGH_U8 ((uint8_t) (11U))

#define BUTTON_NO_OF_NOTIFICATIONS_U8   ((uint8_t) (16U))
#define BUTTON_NO_4_TRANS_2_EXT_HIGH_U8 ((uint8_t) (100U))
#define BUTTON_NO_4_TRANS_2_HIGH_U8     ((uint8_t) (3U))

typedef enum
{
  Low,
  High,
  ExtendedHigh
}T_ButtonState;

typedef struct
{
  T_ButtonState StateOld_t:4;
  T_ButtonState State_t:4;
  uint8_t       Debounce_u8;
}T_ButtonData;

T_ButtonData gButtonData_t[Button_Max];

typedef struct _T_ButtonNotification
{
  T_ButtonNotify Function_pt;
  T_ButtonReason Reason_t;
  T_Button       Button_t;
} T_ButtonNotification;

static T_ButtonNotification gNotifications[BUTTON_NO_OF_NOTIFICATIONS_U8];

static void BcoCheckButtons( void );
static uint8_t BcoGetButtonValue( uint8_t iButton_u8 );
static void BcoHandleNotification( T_ButtonReason iReason_t, T_Button iButton_t );
static uint8_t BcoCheckCapButton(void);

static void BcoCheckButtons( void )
{

  for( uint8_t lCount_u8 = 0; lCount_u8 < Button_Max; lCount_u8++ )
  {
    if( 0 != BcoGetButtonValue( lCount_u8 ) )
    {
      if( BUTTON_NO_4_TRANS_2_EXT_HIGH_U8 <= gButtonData_t[lCount_u8].Debounce_u8 )
      {
        gButtonData_t[lCount_u8].State_t = ExtendedHigh;
      }
      else if( BUTTON_NO_4_TRANS_2_HIGH_U8 <= gButtonData_t[lCount_u8].Debounce_u8 )
      {
        gButtonData_t[lCount_u8].State_t = High;
        gButtonData_t[lCount_u8].Debounce_u8++;
      }
      else
      {
        ++gButtonData_t[lCount_u8].Debounce_u8;
      }
    }
    else
    {
      gButtonData_t[lCount_u8].Debounce_u8 = 0;
      gButtonData_t[lCount_u8].State_t = Low;
    }
    if( gButtonData_t[lCount_u8].State_t != gButtonData_t[lCount_u8].StateOld_t )
    {
      if( ( gButtonData_t[lCount_u8].State_t    == High ) &&
          ( gButtonData_t[lCount_u8].StateOld_t == Low ) )
      {
        BcoHandleNotification( Button_Low2High, lCount_u8 );
        UART_PUT_VALUE("Low2High - ", lCount_u8 );
      }
      else if( ( gButtonData_t[lCount_u8].State_t    == ExtendedHigh ) &&
               ( gButtonData_t[lCount_u8].StateOld_t == High ) )
      {
        BcoHandleNotification( Button_High2ExtHigh, lCount_u8 );
        UART_PUT_VALUE("High2ExtHigh - ", lCount_u8 );
      }
      else if( ( gButtonData_t[lCount_u8].State_t    == Low ) &&
               ( gButtonData_t[lCount_u8].StateOld_t == High ) )
      {
        BcoHandleNotification( Button_High2Low, lCount_u8 );
        UART_PUT_VALUE("High2Low - ", lCount_u8 );
      }
      else if( ( gButtonData_t[lCount_u8].State_t    == Low ) &&
               ( gButtonData_t[lCount_u8].StateOld_t == ExtendedHigh ) )
      {
        BcoHandleNotification( Button_ExtHigh2Low, lCount_u8 );
        UART_PUT_VALUE("ExtHigh2Low - ", lCount_u8 );
      }
      gButtonData_t[lCount_u8].StateOld_t = gButtonData_t[lCount_u8].State_t;
    }
  }
}

static uint8_t BcoGetButtonValue( uint8_t iButton_u8 )
{
  uint8_t lButtonValue_u8 = 0; /* button unpressed */
  switch( iButton_u8 )
  {
    case Button_Back:
    {
      lButtonValue_u8 = !(BUTTON_PORT & (1 << PINC0));
      break;
    }
    case Button_Enter:
    {
      lButtonValue_u8 = !(BUTTON_PORT & (1 << PINC1));
      break;
    }
    case Button_Right:
    {
      lButtonValue_u8 = !(BUTTON_PORT & (1 << PINC2));
      break;
    }
    case Button_Left:
    {
      lButtonValue_u8 = !(BUTTON_PORT & (1 << PINC3));
      break;
    }
    case Button_Cap:
    {
      if( CAP_HIGH_U8 < BcoCheckCapButton() )
      {
        lButtonValue_u8 = 1;
      }
      break;
    }
    default:
    {
      break;
    }
  }
  return (lButtonValue_u8);
}

static uint8_t BcoCheckCapButton(void)
{
  uint8_t i = CAP_MAX;
  uint8_t j = 0;

  /* Set Input pin to input */
  KEYDDR &= ~(1 << KEY0); // input

  for (j = 0; j < CAP_MAX; j++)
  {
    /* check if pin is high */
    if (KEYPIN & 1 << KEY0)
    {
      /* decrement i if high */
      i--;
    }
  }

  /* set to low */
  KEYPORT &= ~(1 << KEY0);
  /* set to output */
  KEYDDR |= (1 << KEY0);

  return (i);
}

void BcoInit( void )
{
  uint8_t lCount_u8;
  for( lCount_u8 = 0; lCount_u8 < BUTTON_NO_OF_NOTIFICATIONS_U8; lCount_u8++ )
  {
    gNotifications[lCount_u8].Function_pt  = NULL;
    gNotifications[lCount_u8].Reason_t     = Button_ReasonNotSet;
    gNotifications[lCount_u8].Button_t     = Button_None;
  }
}

void BcoCyclic( void )
{
  BcoCheckButtons( );
}

void BcoRegisterNotification( T_ButtonNotify iFunc_pt, T_ButtonReason iReason_t, T_Button iButton_t )
{
  uint8_t lCount_u8 = 0;
  for( lCount_u8 = 0; lCount_u8 < BUTTON_NO_OF_NOTIFICATIONS_U8; lCount_u8++ )
  {
    if( ( gNotifications[lCount_u8].Function_pt  == NULL                ) &&
        ( gNotifications[lCount_u8].Reason_t     == Button_ReasonNotSet ) &&
        ( gNotifications[lCount_u8].Button_t     == Button_None         ) )
    {
      if( ( iFunc_pt  != NULL                ) &&
          ( iReason_t != Button_ReasonNotSet ) &&
          ( iButton_t != Button_None         ) )
      {
        gNotifications[lCount_u8].Function_pt = iFunc_pt;
        gNotifications[lCount_u8].Reason_t    = iReason_t;
        gNotifications[lCount_u8].Button_t    = iButton_t;
        break;
      }
    }
  }
}

void BcoUnregisterNotification( T_ButtonNotify iFunc_pt, T_ButtonReason iReason_t, T_Button iButton_t )
{
  uint8_t lCount_u8 = 0;
  for( lCount_u8 = 0; lCount_u8 < BUTTON_NO_OF_NOTIFICATIONS_U8; lCount_u8++ )
  {
    if( ( gNotifications[lCount_u8].Function_pt  == iFunc_pt  ) &&
        ( gNotifications[lCount_u8].Reason_t     == iReason_t ) &&
        ( gNotifications[lCount_u8].Button_t     == iButton_t ) )
    {
        gNotifications[lCount_u8].Function_pt = NULL;
        gNotifications[lCount_u8].Reason_t    = Button_ReasonNotSet;
        gNotifications[lCount_u8].Button_t    = Button_None;
    }
  }
}

static void BcoHandleNotification( T_ButtonReason iReason_t, T_Button iButton_t )
{
  uint8_t lCount_u8 = 0;
  for( lCount_u8 = 0; lCount_u8 < BUTTON_NO_OF_NOTIFICATIONS_U8; lCount_u8++ )
  {
    if( ( gNotifications[lCount_u8].Reason_t     == iReason_t ) &&
        ( gNotifications[lCount_u8].Button_t     == iButton_t ) &&
        ( gNotifications[lCount_u8].Function_pt  != NULL      ) )
    {
      gNotifications[lCount_u8].Function_pt( iReason_t );
    }
    else if(( gNotifications[lCount_u8].Reason_t     == iReason_t ) &&
            ( gNotifications[lCount_u8].Button_t     == Button_All ) &&
            ( gNotifications[lCount_u8].Function_pt  != NULL      ) )
    {
      gNotifications[lCount_u8].Function_pt( iReason_t );
    }
    else if(( gNotifications[lCount_u8].Reason_t     == Button_AllReasons ) &&
            ( gNotifications[lCount_u8].Button_t     == iButton_t         ) &&
            ( gNotifications[lCount_u8].Function_pt  != NULL              ) )
    {
      gNotifications[lCount_u8].Function_pt( iReason_t );
    }
    else if(( gNotifications[lCount_u8].Reason_t     == Button_AllReasons ) &&
            ( gNotifications[lCount_u8].Button_t     == Button_All         ) &&
            ( gNotifications[lCount_u8].Function_pt  != NULL              ) )
        {
          gNotifications[lCount_u8].Function_pt( iReason_t );
        }
  }
}
